//
//  PublishFeedController.swift
//  发布动态界面
//
//  Created by smile on 2019/4/19.
//  Copyright © 2019 ixuea. All rights reserved.
//

import UIKit

//TextView实现提示文字框架
import KMPlaceholderTextView

//发布订阅框架
import SwiftEventBus

//图片选择
import PhotoSolution

// 键盘管理器
// 目的是键盘弹出时
// 相应的控件自动上移避免挡住
import KeyboardLayoutGuide

class PublishFeedController: BaseTitleController {
    
    /// 文本输入框
    @IBOutlet weak var tvContent: KMPlaceholderTextView!
    
    /// 显示图片的列表控件
    @IBOutlet weak var collectionView: UICollectionView!
    
    /// 显示字数统计的label
    @IBOutlet weak var lbCount: UILabel!
    
    /// 顶部的快捷按钮容器
    /// 找到他的目的是
    /// 实现弹出键盘自动上移
    /// 避免被遮挡
    @IBOutlet weak var svInfoContainer: UIStackView!
    
    /// 显示当前位置的label
    @IBOutlet weak var lbLocation: UILabel!
    
    /// 输入的内容
    var content:String!
    
    /// 选择的图片列表
    var dataArray:[Any] = []
    
    /// 高德地图位置管理器
    var locationManager:AMapLocationManager!
    
    /// 位置
    var location:CLLocation?
    
    /// geo对象
    var geo:AMapLocationReGeocode?
    
    
    override func initViews() {
        super.initViews()
        
        setTitle("发动态")
        
        //设置提示文本
        tvContent.placeholder="人生苦短，我们只做好课！\n千言万语还来不说."
        
        //创建右侧保存按钮
        let saveBarItem = UIBarButtonItem(title: "保存", style: .plain, target: self, action: #selector(onSaveClick(sender:)))
    
        //将BarItem设置到导航栏右侧
        navigationItem.rightBarButtonItem=saveBarItem
        
        //添加一条自动布局约束
        //含义是：输入框的底部锚点
        //等于当前界面中View的底部锚点
        //也就说这个框架
        svInfoContainer.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor).isActive = true
    }
    
    override func initDatas() {
        super.initDatas()
        
        //初始高德地图定位
        initLocation()
        
        setData([])
    }
    
    func initLocation() {
        locationManager=AMapLocationManager()
        
        //带逆地理信息的一次定位（返回坐标和地址信息）
        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
        
        //定位超时时间，最低2s，此处设置为2s
        locationManager.locationTimeout = 2
        
        //逆地理请求超时时间，最低2s，此处设置为2s
        locationManager.reGeocodeTimeout = 2
    
        //启动定位
        startLocation()
    }
    
    /// 开始定位
    /// 虽然模拟器也可以模拟定位
    /// 但比较麻烦
    /// 所以建议大家使用真机
    func startLocation() {
        //请求位置
        locationManager.requestLocation(withReGeocode: true) { (location, reGeocode, error) in
            if let error = error {
                //有错误
                let error = error as NSError
                
                if error.code == AMapLocationErrorCode.locateFailed.rawValue {
                    //定位错误：此时location和regeocode没有返回值，不进行annotation的添加
                    print("PublishFeedController location error:\(error.code), \(error.localizedDescription)")
                    return
                } else if error.code == AMapLocationErrorCode.reGeocodeFailed.rawValue
                    || error.code == AMapLocationErrorCode.timeOut.rawValue
                    || error.code == AMapLocationErrorCode.cannotFindHost.rawValue
                    || error.code == AMapLocationErrorCode.badURL.rawValue
                    || error.code == AMapLocationErrorCode.notConnectedToInternet.rawValue
                    || error.code == AMapLocationErrorCode.cannotConnectToHost.rawValue {
                    //逆地理错误：在带逆地理的单次定位中，逆地理过程可能发生错误，此时location有返回值，regeocode无返回值，进行annotation的添加
                    print("PublishFeedController re geo error:\(error.code), \(error.localizedDescription)")
                }else{
                    print("PublishFeedController location unknown error:\(error)")
                }
                
            }else {
                //没有错误：location有返回值
                if let location = location {
                    print("PublishFeedController location success:\(location)")
                    self.onLocationSucces(location)
                }
                
                //regeocode是否有返回值取决于是否进行逆地理操作
                if let reGeocode = reGeocode {
                    print("PublishFeedController re geo success:%@", reGeocode)
                    
                    self.onGeoSucces(reGeocode)
                }
                
            }
            
            
        }
    }
    
    func onLocationSucces(_ data:CLLocation) {
        //保存到实例变量
        //发送动态的时候会用到
        location=data
    }
    
    func onGeoSucces(_ data:AMapLocationReGeocode) {
        //逆地理成功
        geo=data
        
        //显示到界面上
        //这里只显示到市就行了
        //模拟器上data里面的字段可能为空
        lbLocation.text="当前位置: \(data.province ?? "")-\(data.city ?? "")"
        
        //真实项目中
        //会添加一个选项
        //然用户选中是否发送地理位置
        //这里就不再实现这么复杂了
    }
    
    override func initListeners() {
        super.initListeners()
        
        //设置代理
        tvContent.delegate=self
    }

    func setData(_ datas:[UIImage]) {
        dataArray.removeAll()
        
        if datas.count<9 {
            //没有选9张图片
            //显示添加按钮
            dataArray=dataArray+datas
            dataArray.append("AddImage")
        }
        collectionView.reloadData()
    }
    
    func selectImage()  {
        //初始化选择图片框架
        let photoSolution = PhotoSolution()
        photoSolution.delegate = self
        
        //返回的图片要压缩
        //不然太大了
        photoSolution.customization.returnImageSize = .compressed
       
        //选择最大9张图片
        present(photoSolution.getPhotoPicker(maxPhotos: 9), animated: true, completion: nil)
    }
    
    
    /// 上传图片
    ///
    /// - Parameter data: data description
    func uploadImage(_ images:[UIImage]) {
        let oss=IxueaOSSUtil.shared()
        
        var results:[Dictionary<String,String>]=[]
        
        //显示进度条
        ToastUtil.showLoading("上传图片中.")
        
        
        //在异步任务中上传图片
        DispatchQueue.global().async {
            //这里是子线程
            
            for data in images {
                let putObjectRequest = OSSPutObjectRequest()
                putObjectRequest.bucketName=ALIYUN_OSS_BUCKET_NAME
                
                //上传
                //如果没有特殊需求建议不要分目录
                //因为当请求达到一定量级会有性能影响
                //如果一定要分目录
                //不要让目录名前面连续
                //例如时间戳倒过来
                let destinationFileName="\(UUIDUtil.getUUID()).jpg"
                putObjectRequest.objectKey=destinationFileName
                
                //将图片转为data数据
                //指令为50%
                //值越小质量越差
                //体积越小
                let imageData=data.jpegData(compressionQuality: 0.5)
                
                //直接上传NSData
                putObjectRequest.uploadingData = imageData!
                
                //上传进度
                putObjectRequest.uploadProgress={
                    bytesSent,totalByteSent,totalBytesExpectedToSend in
                    
                    //这里没用到
                    //所有就打印日志
                    //真实项目中可能会显示到图片上
                    print("PublishFeedController upload image progress:\(bytesSent) totalByteSent:\(totalByteSent) totalBytesExpectedToSend:\(totalBytesExpectedToSend)")
                }
                
               let putTask = oss.putObject(putObjectRequest)
                
                //上传完回调
                putTask.continue({ (task) -> Any? in
                    if task.error == nil {
                        print("PublishFeedController upload image success")
                        
                    } else {
                        //上传失败了
                        //真实项目可能会提示给用户
                        print("PublishFeedController upload image failed,error:\(task.error)")
                    }
                    
                    return nil
                })
                
                //同步等待
                putTask.waitUntilFinished()
                
                //上传成功后
                //将图片名称
                //放到数组中
                results.append(["uri":destinationFileName])
            }
            
            //所有图片上传完成
            DispatchQueue.main.async {
                //这里是主线程
                
                //不管上传失败还是成功
                //都要隐藏进度条
                ToastUtil.hideLoading()
                
                //判断是否上传完成
                if images.count==results.count {
                    //数量相等
                    //表示上传完成
                    self.saveFeed(results)
                } else {
                    //上传失败
                    //真实项目中可以会提供重新上传
                    ToastUtil.short("上传图片出错，请稍后再试!")
                }
                
            }
        }
        
    }
    
    //获取选择图片
    func getSelectedImages() -> [UIImage] {
        var result:[UIImage]=[]
        for data in dataArray {
            if data is UIImage {
                //只添加UIImage
                //也就是过滤掉添加按钮
                result.append(data as! UIImage)
            }
        }
        
        return result
    }

    /// 保存动态
    func saveFeed(_ images:[Dictionary<String,String>]?) {
        
        //添加上位置信息
        var longitude:Double?
        var latitude:Double?
        
        if let location = location {
            //有地理位置
            
            //添加经纬度
            //因为后台如果要实现搜索附近的人等功能
            //就需要根据经纬度
            
            //经纬度不会在动态列表中返回
            //所以大家不用担心隐私问题
            let coordinate=location.coordinate
            
            //经度
            longitude=coordinate.longitude
            
            //纬度
            latitude=coordinate.latitude
        }else{
            //没有地理位置
            //我们这里也允许发送动态
        }
        
        var province:String?
        var city:String?
        
        //添加geo信息
        if let data = geo {
            //geo信息主要用来显示
            //或者直接查询也要保存
            //这里还是只保存省和市
            
            //目的是避免泄漏大家的隐私
            //因为我们这里的动态是所有人都能看
            
            //省
            province=data.province
            
            //市
            city=data.city
        }else{
            //没有geo信息
            //我们这里也允许发送动态
        }
        
        Api.shared.createFeed(content: content!, province: province, city: city, longitude: longitude, latitude: latitude, images: images).subscribeOnSuccess { (data) in
            //发送通知
            //列表界面刷新
            SwiftEventBus.post(PUBLISH_FEED, sender: nil)
            
            //关闭当前界面
            self.navigationController?.popViewController(animated: true)
            
        }

    }
    
    /// 发布动态
    ///
    /// - Parameter sender: <#sender description#>
    @objc func onSaveClick(sender:UIButton) {
        print("PublishFeedController onSaveClick")
        
        content=tvContent.text.trim()
        if content.isEmpty {
            ToastUtil.short("请输入内容.")
            return
        }
        
        if content.length > 140 {
            ToastUtil.short("内容不能超过140字符.")
            return
        }
        
        let selectedImages=getSelectedImages()
        if selectedImages.count>0 {
            //有图片，先上传图片
            uploadImage(selectedImages)
        } else {
            //没有图片，直接发送动态
            saveFeed(nil)
        }
    }
    
    
    /// 快捷按钮栏 选择图片按钮点击
    ///
    /// - Parameter sender: <#sender description#>
    @IBAction func onSelectImageClick(_ sender: UIButton) {
        selectImage()
    }

}

// MARK: - 列表数据源和代理
extension PublishFeedController:UICollectionViewDataSource,UICollectionViewDelegate{
    /// 返回有多个条目
    ///
    /// - Parameters:
    ///   - collectionView: <#collectionView description#>
    ///   - section: <#section description#>
    /// - Returns: <#return value description#>
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return dataArray.count
    }
    
    /// 返回当前位置的Cell
    ///
    /// - Parameters:
    ///   - collectionView: <#collectionView description#>
    ///   - indexPath: 当前位置
    /// - Returns: 返回当前位置对应的Cell
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        
        //取出当前位置对应的数据
        let data=dataArray[indexPath.row]
        
        let cell=collectionView.dequeueReusableCell(withReuseIdentifier: CELL, for: indexPath) as! SelectImageCell
        
        cell.tag=indexPath.row
        
        cell.bindData(data)
        
        //删除当前图片回调
        cell.deleteImageClick={
            index in
            self.dataArray.remove(at: index)
            self.collectionView.reloadData()
        }
        
        return cell
        
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        //取出当前位置对应的数据
        let data=dataArray[indexPath.row]
        
        if data is String {
            //添加按钮才能点击
            self.selectImage()
        }
    }
}


// MARK: - UITextView代理
// 主要用来监听输入文本数量
// 限制最大字数
extension PublishFeedController:UITextViewDelegate{
    
    /// 当前文本改变时调用
    ///
    /// - Parameters:
    ///   - textView: <#textView description#>
    ///   - range: <#range description#>
    ///   - text: <#text description#>
    /// - Returns: <#return value description#>
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        print("PublishFeedController shouldChangeTextInRange:\(text)")
        
        let count=tvContent.text.count
        lbCount.text="\(count)/140"
        
        if count>=140 && !text.isEmpty {
            //超出长度了
            //并且输入的内容不为空
            
            //不为空是过滤掉删除键
            //因为当按删除键时
            //内容就是空
            return false
        }
        
        return true
    }
}

// MARK: - 选择图片框架代理
extension PublishFeedController:PhotoSolutionDelegate{
    /// 图片选择成功
    ///
    /// - Parameter images: <#images description#>
    func returnImages(_ images: [UIImage]) {
        print("PublishFeedController select image count:\(images.count)")
        setData(images)
    }
    
    
    /// 取消选择
    func pickerCancel() {
        
    }
}

// MARK: - UICollectionViewDelegateFlowLayout代理
extension PublishFeedController:UICollectionViewDelegateFlowLayout{
    /// 返回当前Cell的大小
    ///
    /// - Parameters:
    ///   - collectionView: <#collectionView description#>
    ///   - collectionViewLayout: <#collectionViewLayout description#>
    ///   - indexPath: <#indexPath description#>
    /// - Returns: <#return value description#>
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        //获取到CollectionView的宽
        let collectionViewWidth=self.collectionView.frame.width
        
        //根据类型计算宽高
        let width=(collectionViewWidth-SIZE_LARGE_DIVIDER*2)/3
        
        return CGSize(width: width, height: width)
    }
}
